/* Copyright 2012 The Go Authors.  All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */
(function() {
"use strict";

var slides, editor, preCode, postCode, $editor, $output;
var slide = null;
var slidenum = 0;
var completed = 0;

var start = 0;
var timer;

// manage translations
function L(k) {
	if (tr[k]) {
		return tr[k];
	} else {
		console.log("translation missing for: "+k);
		return "(no translation for "+k+")";
	}
}

function init() {
	slides = $("div.slide");
	slides.each(function(i, slide) {
		var $s = $(slide).hide();

		var $h2 = $s.find("h2").first();
		var $nav;
		if ($h2.length > 0) {
			$("<div/>").addClass("clear").insertAfter($h2);
			$nav = $("<div/>").addClass("nav");
			if (i == 0) addNextButton($nav);
			$nav.insertBefore($h2);
		}
	});

	// set up playground editor
	editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
		theme: "default",
		matchBrackets: true,
		indentUnit: 4,
		tabSize: 4,
		indentWithTabs: false,
		mode: "text/x-go",
		lineNumbers: true,
		extraKeys: {
			"Shift-Enter": function() {
				run();
			}
		}
	});
	$editor = $(editor.getWrapperElement()).attr('id', 'code');
	$output = $('#output');

	$('#more').click(function() {
		$('.controls').toggleClass('expanded');
		return false;
	});
	$('html').click(function() {
		$('.controls').removeClass('expanded');
	});

	$('#run').click(function() {
		run();
		$('.controls').removeClass('expanded');
		return false;
	});

	$('#reset').click(function() {
		reset();
		$('.controls').removeClass('expanded');
		return false;
	});

	$('#kill').click(function() {
		kill();
		$('.controls').removeClass('expanded');
		return false;
	});

	$('#format').click(function() {
		format();
		$('.controls').removeClass('expanded');
		return false;
	});

	$('#togglesyntax').click(function() {
		if (editor.getOption('theme') === 'default') {
			editor.setOption('theme', 'plain');
			$('#togglesyntax').text(L('syntax')+': '+L('off'));
		} else {
			editor.setOption('theme', 'default');
			$('#togglesyntax').text(L('syntax')+': '+L('on'));
		}
		setcookie('theme', editor.getOption('theme'), 14);
		$('.controls').removeClass('expanded');
		return false;
	});

	$('#togglelineno').click(function() {
		if (editor.getOption('lineNumbers')) {
			editor.setOption('lineNumbers', false);
			$('#togglelineno').text(L('lineno')+': '+L('off'));
		} else {
			editor.setOption('lineNumbers', true);
			$('#togglelineno').text(L('lineno')+': '+L('on'));
		}
		setcookie('lineno', editor.getOption('lineNumbers'), 14);
		$('.controls').removeClass('expanded');
		return false;
	});

	if (getcookie('lineno') != ""+editor.getOption('lineNumbers')) {
		$('#togglelineno').trigger('click');
	} else {
		$('#togglelineno').text(L('lineno')+': '+L('on'));
	}

	if (getcookie('theme') != ""+editor.getOption('theme')) {
		$('#togglesyntax').trigger('click');
	} else {
		$('#togglesyntax').text(L('syntax')+': '+L('on'));
	}

	// set these according to lang.js
	$('#run').text(L('run'));
	$('#reset').text(L('reset'));
	$('#format').text(L('format'));
	$('#kill').text(L('kill'));
	$('#tocbtn').attr('title', L('toc'));
	$('#run').attr('title', L('compile'));
	$('#more').attr('title', L('more'));

	document.onkeydown = pageUpDown;
	show(0);
}

function addNextButton($nav) {
	$nav.append($("<a>▶</a>").click(function() {
		show(slidenum+1);
		return false;
	}));
}

function startTimer() {
	if (start != 0) return;
	start = new Date().getTime();
	timer = setInterval(function() {
		var delta = new Date().getTime() - start;
		$('#timer').text(timeString(delta));
	}, 100);
}

function stopTimer() {
	clearInterval(timer);
}

function timeString(ms) {
	var d = Math.floor(ms / 100);
	var s = "";

	// Tenths of second.
	s = "." + numString(d % 10, 1);
	d = Math.floor(d/10);

	// Seconds.
	s = ":" + numString(d % 60, 2) + s
	d = Math.floor(d/60)

	// Minutes.
	s = numString(d % 60, 2) + s
	d = Math.floor(d/60)

	// Hours (only when necessary).
	if (d > 0) s = d + ":" + s

	return s
}

function numString(n, zeroes) {
	var s = ""+n;
	while (s.length < zeroes) s = "0" + s;
	return s;
}

function show(i) {
	if (i < 0 || i >= slides.length) return;

	if (i > 0) startTimer();

	// if a slide is already onscreen, hide it and store its code
	if(slide !== null) {
		var $oldSlide = $(slide).hide();
		if (!$oldSlide.hasClass("nocode")) {
			save(slidenum);
		}
	}

	$('#toc').hide();
	$('#slides').show();

	// switch to new slide
	slidenum = i;
	slide = slides[i];
	var $s = $(slide).show();

	// load stored code, or hide code box
	if ($s.hasClass("nocode")) {
		$('#workspace').hide();
		$('#wrap').addClass('full-width');
	} else {
		$('#wrap').removeClass('full-width');
		$('#workspace').show();
		$output.empty();

		var orig = slideCode($s);
		var loaded = load(i);
		if (loaded && loaded != orig) {
			editor.setValue(loaded); 
		} else {
			editor.setValue(orig); 
		}

		editor.focus();
	}
}

function slideCode($s) {
	preCode = "";
	postCode = "";

	var $src = $s.find('div.source pre');
	var orig = "";
	var seenOrig = false;
	$src.each(function() {
		var t = $(this).text();
		if ($(this).attr("style") == "display: none") {
			if (seenOrig) {
				postCode += t;
				return;
			} 
			preCode += t;
			return
		}
		seenOrig = true
		orig += t;
	});
	return orig;
}

function reset() {
	editor.setValue(slideCode($(slide)));
	save(slidenum);
}

var pageData = {};

function save(page) {
	pageData[page] = editor.getValue();
	return true;
}

function load(page) {
	var data = pageData[page];
	if (data) {
		return data;
	}
	return false;
}

function pageUpDown(event) {
	var e = window.event || event;
	if (e.keyCode === 33) { // page up
		e.preventDefault();
		show(slidenum-1);
		return false;
	}
	if (e.keyCode === 34) { // page down
		e.preventDefault();
		if (slidenum <= completed) show(slidenum+1);
		return false;
	}
	return true;
}

var transport; // set by initTour
var running;

function body() {
	return editor.getValue();
}

function loading() {
	$output.html('<pre><span class="loading">'+L('waiting')+'</span></pre>');
}

function run() {
	kill();
	loading();
	var b = body();
	var output = PlaygroundOutput($output.find("pre")[0]);
	output = highlightOutput(output);
	output = fixErrors(output, preCode.split("\n").length, b.split("\n").length);
	output = stripHeader(output);
	output = watchForPass(output);
	running = transport.Run(preCode + b + postCode, output);
}

function watchForPass(output) {
	return function(write) {
		if (write.Body && write.Body == "PASS\n" && completed == slidenum-1) {
			if (slidenum == slides.length-2) stopTimer();
			completed = slidenum;
			addNextButton($(slide).find('.nav'));
		}
		output(write);
	}
}

function stripHeader(output) {
	var hdr = "# command-line-arguments\n";
	return function(write) {
		if (write.Body && write.Body.indexOf(hdr) == 0) {
			write.Body = write.Body.substr(hdr.length);
		}
		output(write);
	}
}

function fixErrors(output, codeStart, codeLines) {
	if (preCode === null) {
		return output;
	}
	var errorRe = /^([./a-z0-9]+:)([0-9]+)(: )/;
	var friendlyErrorSent = false;
	return function(write) {
		if (!write.Body) {
			output(write);
			return;
		}
		var lines = write.Body.split("\n");
		for (var i = 0; i < lines.length; i++) {
			var line = lines[i];
			var r = errorRe.exec(line);
			if (r === null) {
				continue;
			}
			var n = r[2]-codeStart+1;
			if (0 < n && n <= codeLines) {
				lines[i] = line.replace(r[0], "prog.go:"+n+r[3]);
				continue;
			}
			if (friendlyErrorSent) {
				lines[i] = "";
				continue;
			}
			friendlyErrorSent = true;
			lines[i] = friendlyError(line.substr(r[0].length));
		}
		write.Body = lines.join("\n");
		output(write);
	}
}

function friendlyError(line) {
	var m;
	var undefRe = /^undefined: (.+)$/;
	if ((m = undefRe.exec(line)) !== null) {
		return 'Function must be named "'+m[1]+'" (case-sensitive)';
	}
	var badTypeRe = /^cannot use .* as type (.+) in assignment$/;
	if ((m = badTypeRe.exec(line)) !== null) {
		return 'Function must be of type '+m[1];
	}
	return line;
}

function highlightOutput(wrappedOutput) {
	return function(write) {
		if (write.Body) highlightErrors(write.Body);
		wrappedOutput(write);
	}
}

function kill() {
	if (running) running.Kill();
}

var seq = 0;

function format() {
	var fakePkg = "package fake\n\n"
	seq++;
	var cur = seq;
	loading();
	$.ajax("/fmt", {
		data: {"body": fakePkg + body()},
		type: "POST",
		dataType: "json",
		success: function(data) {
			if (seq !== cur) {
				return;
			}
			$output.empty();
			if (data.Error) {
				$('<pre class="error" />').text(data.Error).appendTo($output);
			} else {
				var body = data.Body.substr(fakePkg.length);
				editor.setValue(body);
			}
		},
		error: function() {
			$('<pre class="error" />').text(L('errcomm')).appendTo($output);
		}
	});
}

function highlightErrors(text) {
	if (!editor || !text) {
		return;
	}
	var errorRe = /[a-z0-9]+\.go:([0-9]+):/g;
	var result;
	while ((result = errorRe.exec(text)) !== null) {
		var line = result[1]*1-1;
		editor.setLineClass(line, null, 'errLine');
	}
	clearHighlightOnChange();
}

function clearHighlightOnChange() {
	editor.setOption('onChange', function() {
		for (var i = 0; i < editor.lineCount(); i++) {
			editor.setLineClass(i, null, null);
		}
		editor.setOption('onChange', null);
	});
}

function getcookie(name) {
	if (document.cookie.length > 0) {
		var start = document.cookie.indexOf(name + '=');
		if (start >= 0) {
			start += name.length + 1;
			var end = document.cookie.indexOf(';', start);
			if (end < 0) {
				end = document.cookie.length;
			}
			return decodeURIComponent(document.cookie.substring(start, end));
		}
	}
	return null;
}

function setcookie(name, value, expire) {
	var expdate = new Date();
	expdate.setDate(expdate.getDate() + expire);
	document.cookie = name + '=' + encodeURIComponent(value) +
		((expire === undefined) ? '' : ';expires=' + expdate.toGMTString());
}

window.initTour = function(t) {
	transport = t
}

$(document).ready(init);

}());
